module(..., package.seeall)

require'zdc.node'
require'zdc.typeex'
require'zdc.dpl'
require'zdc.dplsvc'
require'zdc.logger'

local Dal = {}
local Dal_mt = {__metatable={}, __index=Dal}

function MakeInhMT(ch)
	ch=ch or {}
	for _i,_j in pairs(Dal) do
		ch[_i]=_j
	end
	return ch
end

function new(gim,gimsvc,log,childmt)
	local repo = setmetatable({gim=gim,gimsvc=gimsvc,logger=logger or zdc.logger.defaultlog}, childmt or Dal_mt)
	repo._hookreg={}
	repo.log=repo.logger
	return repo
end

function InitDAL(dal,dplcfg,dplsvccfg,glbsbenv,log)
	dal=dal
	if dplcfg.fallback then
		local r=zdc.dpl.BuildDpl(dal,dplcfg.fallback,glbsbenv)
		dal:SetFallbackDpl(r)
		if not dplcfg.NOCONFIG then
			r:SetSBENV(dplcfg.fallback.CONFIG_NAME)
		end
	end
	dal.gim:_delall(dal)
	local step2={}
	for i=1,#dplcfg do
		local dc=dplcfg[i]
		if #dc>3 then
			step2[#step2+1]=assert(dal:NewDpl(dc[1],dc[2],dc[3],dc[4]))
		elseif #dc==3 then
			step2[#step2+1]=assert(dal:NewDpl(dc[1],dc[2],dc[2],dc[3]))
		else
			step2[#step2+1]=assert(dal:NewDplEx(dc))
		end
	end
	for i=1,#step2 do
		local v=step2[i]
		if type(v)=='function' then assert(v()) end
	end
	
	dal.gimsvc:_delall(dal)
	if dplsvccfg then
		for i=1,#dplsvccfg do
			local dc=dplsvccfg[i]
			assert(dal:NewDplSvcEx(dc))
		end
	end
	return dal
end

--[[
tagcfg={
	tagdpl={
		{id=id1,wrap=}...
	}
}
--]]
function Dal:NewTagDal(tag,tagcfg)
	local tagdal=self.tagdal
	if not tagdal then
		tagdal={}
		self.tagdal=tagdal
	end
	if tagdal[tag] then return tagdal[tag] end
	if not tagcfg then return nil, 'no config arg' end
	local nd=setmetatable({gimtag=tag},{__index=self})
	for i,v in ipairs(tagcfg.tagdpl) do
		assert(nd.gim:_add_tag_dpl(nd,tag,v.id,v.wrap),'tagcfg error fmt:'..tostring(v.id))
	end
	tagdal[tag]=nd
	nd.dal=nd
	return nd
end

function Dal:SetFallbackDpl(f)
	self._fallbackdpl=f
end
function Dal:AddDpl(idpat,childpat,subpat,dpl)
	return self.gim:_add_dpl(self,idpat,childpat,subpat,dpl)
end
function Dal:AddDplEx(kv,dpl)
	return self.gim:_add_dpl_ex(self,kv,dpl)
end
function Dal:GetDpl(id)
	return self.gim:_get_dpl_by_id(self,id)
end
function Dal:GetGimInfo()
	return self.gim:_get_info(self)
end
function Dal:NewDpl(idpat,childpat,subpat,config)
	local dpl,msg=zdc.dpl.BuildDpl(self,config,self._bak_config.sbenv)
	if dpl then dpl,msg=self:AddDpl(idpat,childpat,subpat,dpl) end
	if not self._bak_config.NOCONFIG then
		if dpl then return function()return dpl:SetSBENV(config.CONFIG_NAME) end end
	end
	return dpl,msg
end
function Dal:NewDplEx(kv)
	local dpl,msg=zdc.dpl.BuildDpl(self,kv.config,self._bak_config.sbenv)
	if dpl then dpl,msg=self:AddDplEx(kv,dpl) end
	if not self._bak_config.NOCONFIG then
		if dpl then return function()return dpl:SetSBENV(kv.config.CONFIG_NAME) end end
	end
	return dpl,msg
end
function Dal:NewDplSvcEx(kv)
	local dpl,msg=zdc.dplsvc.BuildDpl(self,kv.config)
	if dpl then dpl,msg=self.gimsvc:_add_dpl_ex(self,kv,dpl) end
	return dpl,msg
end
function Dal:ResetCache(force)
	self.gim:_do_every_dpl(self,function (dpl) dpl:_reset_cache(force) end)
end
function Dal:RemoveCache(id,deep)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then dpl:_del_cache(id,deep) end
	if deep then
		local dpls=self.gim:_get_sub_dpls(self,id)
		for idx=1,#dpls do
			if dpls[idx]~=dpl then	dpls[idx]:_del_cache(id,true) end
		end
	end
end

function Dal:DropAll(opt)
	self.gim:_do_every_dpl(self,function (dpl) dpl:_drop_all(opt) end)
end

function Dal:AbortTA()
	self.gim:_do_every_dpl(self,function (dpl) dpl:_ta_abort() end)
end
function Dal:CommitTA(opt)
	local alllst={}
	self.gim:_do_every_dpl(self,function (dpl)
		local a,b=dpl:_ta_commit(opt)
		if a or b then table.insert(alllst,{a,b}) end
	end)
	return alllst
end
function Dal:EndTA(r)
	if r then self:CommitTA() else self:AbortTA() end
end
function Dal:InTA()
	return self.gim:_do_every_dpl(self,function (dpl)
		if dpl:_ta_inta() then return true end 
	end)
end

function Dal:HasNode(id)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	return dpl and dpl:_has_node(id)
end

function Dal:GetNode(id)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then return dpl:_get_node(id) end
	if self._fallbackdpl then return self._fallbackdpl:_get_node(id) end
end

function Dal:CallNode(user,id,act,...)
	local dpl
	if self.gimsvc then
		dpl=self.gimsvc:_get_svcdpl_by_id(self,id)
	end
	if not dpl then
		dpl=self.gim:_get_svcdpl_by_id(self,id)
	end
	if dpl then return dpl:_call_node(user,id,act,...) end
	if self._fallbackdpl then return self._fallbackdpl:_call_node(user,id,act,...) end
end
function Dal:ACallNode(user,cb,id,act,...)
	local dpl
	if self.gimsvc then
		dpl=self.gimsvc:_get_svcdpl_by_id(self,id)
	end
	if not dpl then
		dpl=self.gim:_get_svcdpl_by_id(self,id)
	end
	if dpl then return dpl:_acall_node(user,cb,id,act,...) end
	if self._fallbackdpl then return self._fallbackdpl:_acall_node(user,cb,id,act,...) end
end

function Dal:MakeNode(id,data,ik,st,firstcreate)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then return dpl:_make_node(id,data,ik,st,firstcreate) end
	if self._fallbackdpl then return self._fallbackdpl:_make_node(id,data,ik,st,firstcreate) end
end

function Dal:SureNode(id,mc,nocache)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then return dpl:_sure_node(id,mc,premake) end
	if self._fallbackdpl then return self._fallbackdpl:_sure_node(id,mc,nocache) end
end

-- return {id1=n1,id2=n2,...}
function Dal:GetChildNodes(parentid)
	local pn=self:GetNode(parentid)
	if pn then return pn:__get_child_all() end
	return self:GetChildNodesFromDpl(parentid)
end

function Dal:GetChildNodesFromDpl(parentid,t)
	t=t or {}
	local dpl=self.gim:_get_dpl_by_parent(self,parentid)
	if dpl then
		t=dpl:_get_child_nodes(parentid,t)
	end
	return t
end
--[[
ikwhere: 'AND(GT(ts,1002321),LE(ik,20))'
sort: 'ts,ik-'
limit: max count of nodes, or offset,maxcnt
return {id1=n1,id2=n2,...}
--]]
function Dal:GetChildNodesEx(parentid,where,sort,limit)
	local t,lst={}
	local dpl=self.gim:_get_dpl_by_parent(self,parentid)
	if dpl then
		t,lst=dpl:_get_child_nodes_ex(parentid,t,where,sort,limit)
	end
	return t,lst
end

function Dal:GetChildRawFieldsEx(parentid,fields,where,sort,limit)
	local dpl=self.gim:_get_dpl_by_parent(self,parentid)
	if dpl then
		t=dpl:_get_child_rawfields_ex(parentid,fields,where,sort,limit)
	end
	return t
end

function Dal:GetChildIdsEx(parentid,where,sort,limit)
	local t
	local dpl=self.gim:_get_dpl_by_parent(self,parentid)
	if dpl then
		t=dpl:_get_child_ids_ex(parentid,where,sort,limit)
	end
	return t
end

function Dal:GetChildCntEx(parentid,where)
	local dpl=self.gim:_get_dpl_by_parent(self,parentid)
	if dpl then
		return dpl:_get_child_cnt_ex(parentid,where)
	end
	return nil,'no dpl'
end

function Dal:RemoveNodeTA(id,deep)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then dpl:_ta_remove_node(id,deep) end
	if deep then
		local dpls=self.gim:_get_sub_dpls(self,id)
		for idx=1,#dpls do
			if dpls[idx]~=dpl then	dpls[idx]:_ta_remove_subnodes(id) end
		end
	end
end

function Dal:RemoveSubNodesTA(parent)
	local dpls=self.gim:_get_sub_dpls(self,parent)
	for idx=1,#dpls do
		if dpls[idx]~=dpl then	dpls[idx]:_ta_remove_subnodes(parent) end
	end
end

function Dal:RemoveNode(id,deep)
	local dpl=self.gim:_get_dpl_by_id(self,id)
	if dpl then dpl:_remove_node(id,deep) end
	if deep then
		local dpls=self.gim:_get_sub_dpls(self,id)
		for idx=1,#dpls do
			if dpls[idx]~=dpl then	dpls[idx]:_remove_subnodes(id) end
		end
	end
end

function Dal:RemoveSubNodes(parent)
	local dpls=self.gim:_get_sub_dpls(self,parent)
	for idx=1,#dpls do
		if dpls[idx]~=dpl then	dpls[idx]:_remove_subnodes(parent) end
	end
end
-----------------------------------------------------------------------------
function Dal:_reg_hook(hkf,v)
	self._hookreg[hkf]=v or true
end

function Dal:_del_hook(hkf)
	self._hookreg[hkf]=nil
end
function Dal:_post_chglist(dpl,rec,dbrec)
	print('\n',rec:tostring())
	print('\ndb chg\n',dbrec:tostring())
	for k,v in pairs(self._hookreg) do
		--local r,m=pcall(k,v,tree,chglist,dbchg)
		local r,m=xpcall(function () return k(self,dpl,v,rec,dbrec) end,debug.traceback)
		if not r then
			print('post chg exception:',m)
		end
	end
end